/* 
	PROGRAM: Thanatos
	AUTHOR: Dwyer, Patrick; Easton, Sean; Roy, Bobby 
	FSU GARNET ID: pmd04; sde07; rcr05e
	COURSE INFORMATION: CNT5412
	Due Date: 04/19/2011
	
	SUMMARY 
		The implementation of the FreenetNode class
*/

#include "FreenetNode.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <iostream>
#include <sstream>
#include <errno.h>
#include <string.h>

using namespace std;

//const string FreenetNode::NAME; // = "ThanatosSpider";

//const string FreenetNode::FREENET_VERSION; // = "2.0";

const int FreenetNode::MAX_BUFFER_LEN;  // 4096 or 4KB or 2^12

FreenetNode::FreenetNode()
{
  
}

FreenetNode::FreenetNode(string ip, string port) : Node(ip, port)
{

}

FreenetNode::FreenetNode(struct sockaddr_in addr, vector<FreenetNode*> peers)
{

}

FreenetNode::FreenetNode(const FreenetNode& rhs)
{

}

FreenetNode::~FreenetNode()
{

}

int FreenetNode::Connect()
{
  char buffer[MAX_BUFFER_LEN];
  // assume m_ipAddress and m_portNum are valid in this function

  // connect to actual node represented by the address
  struct addrinfo hints;
  struct addrinfo * results, * i;
  bool failedConnect = true;
  bzero((void *) &hints, sizeof(hints));
  bzero((void *) &m_address, sizeof(struct sockaddr_in));
  hints.ai_family = AF_INET;
  //hints.ai_socktype = SOCK_STREAM;

  if (getaddrinfo(m_ipAddress.c_str(), m_portNum.c_str(), &hints, &results) != 0)
  {
    cerr << "Error in Node(string, string) at " << __LINE__ << ": "
	 << strerror(errno) << endl;
  }
  struct sockaddr_in * tmp;
  for (i = results; i != NULL; i = i->ai_next)
  {
    if (i->ai_family == AF_INET)
    {
      // grab the IPv4 connection
      tmp = (struct sockaddr_in *) i->ai_addr;
    }
    else
    {
      continue; // IPv6 so get a different connection
    }
    // create the socket
    m_socketDesc = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
    if (m_socketDesc == -1)
    {
      // failed to create a socket
      continue;
    }

    cerr << i->ai_family << " " << i->ai_socktype << " "
	 << inet_ntop(tmp->sin_family, (void *)&tmp->sin_addr, buffer, 100)
    	 << " " << ntohs(tmp->sin_port) << endl;
    // get the actual connection information
    m_address.sin_addr.s_addr = tmp->sin_addr.s_addr;
    m_address.sin_family = tmp->sin_family;
    m_address.sin_port = tmp->sin_port;
    
    // attempt the actual connection
    if (connect(m_socketDesc, (struct sockaddr*)&m_address, sizeof(m_address) 
		== 0))
    {
      cout << "Connect at: " 
	   << inet_ntop(tmp->sin_family, (void *)&tmp->sin_addr, 
			buffer, MAX_BUFFER_LEN)
	   << " " << ntohs(tmp->sin_port) << endl;
      failedConnect = false;
      break; // we have a good connection so break
    }
    else
    {
      cerr << "Error in Connect() at " << __LINE__ << ": "
	   << strerror(errno) << endl;
      continue; // connection failed; try again
    }
  }
  freeaddrinfo(results);

  if (failedConnect)
  {
    bzero((void *) &m_address, sizeof(m_address));
    return -1;
  }

  // now we can send the first ClientHello through the socket
  // create the message
  string clientHello = "ClientHello\n";
  clientHello.append("Name=").append(NAME).append("\n");
  clientHello.append("ExpectedVersion=").append(FREENET_VERSION).append("\n");
  clientHello.append("EndMessage\n");
  // send the message
  cout << clientHello;
  if (write(m_socketDesc, clientHello.c_str(), clientHello.size()) == -1)
  {
    // errno should be set here
    cerr << "Error in Connect() at " << __LINE__ << ": " 
	 << strerror(errno) << endl;
    return -1;
  }

  // now we expect a NodeHello message
  int bufferLen = 0;
  bufferLen = read(m_socketDesc, buffer, MAX_BUFFER_LEN);
  if (bufferLen == -1)
  {
    cerr << "Error in Connect() at " << __LINE__ << ": " 
	 << strerror(errno) << endl;
    return -1;
  }
  // make buffer into a typical c-string
  buffer[bufferLen] = '\0';
  cerr << "Connect message received: " << buffer << endl;

  // for now assume it's actually a NodeHello message and it worked

  return 0;  // 0 indicates success
}

int FreenetNode::Disconnect()
{
  // first we need to tell the node we are disconnecting
  string dc = "Disconnect\nEndMessage\n";
  if (write(m_socketDesc, dc.c_str(), dc.size()) == -1)
  {
    // errno should be set here if we need to check it
    cerr << "Error in Disconnect() at " << __LINE__ << ": " 
	 << strerror(errno) << endl;
    return -1;
  }

  // the node will not respond with a message, so just close the socket
  if (close(m_socketDesc) == -1)
  {
    cerr << "Error in Disonnect() at " << __LINE__ << ": " 
	 << strerror(errno) << endl;
    return -1;  // again errno should be set 
  }
  return 0;
}

vector<Node*> FreenetNode::GetPeers()
{
  // here we assume that the socket is in a valid state and the connect did not fail
  string listPeers = "ListPeers\nWithMetadata=true\nWithVolatile=true\nEndMessage\n";

  if (write(m_socketDesc, listPeers.c_str(), listPeers.size()) == -1)
  {
    // m_peers should be empty, so we just return an empty list on failure
    cerr << "Error in GetPeers() at " << __LINE__ << ": " 
	 << strerror(errno) << endl;
    return m_peers;
  }

  // now we expect to get a bunch of Peer messages
  char msgBuffer[MAX_BUFFER_LEN];
  int msgLen = 0;
  msgBuffer[msgLen] = '\0';
  msgLen = read(m_socketDesc, msgBuffer, MAX_BUFFER_LEN);
  if (msgLen == -1)
  {
    // read failed here
   cerr << "Error in GetPeers() at " << __LINE__ << ": " 
	 << strerror(errno) << endl;
    return m_peers;
  }
  // make msgBuffer into valid c-string
  //  msgBuffer[bufferLen] = '\0';
  msgBuffer[msgLen] = '\0';
    cerr << "GetPeers message received: " << '\n' << msgBuffer << endl;

  // now msgBuffer should have a Peer or EndListPeers message
  // so parse it and add a new node
  while (parseMessage(msgBuffer, msgLen) != "EndListPeers")
  {
    msgLen = read(m_socketDesc, msgBuffer, MAX_BUFFER_LEN);     //loop hangs here
    if (msgLen == -1)
    {
      // read failed here
      cerr << "Error in GetPeers() at " << __LINE__ << ": " 
	   << strerror(errno) << endl;
      return m_peers;
    }
    // make msgBuffer into valid c-string
    msgBuffer[msgLen] = '\0';
    //    cout << "GetPeers message received: " << msgBuffer << endl;
  }
  return m_peers;
}

string FreenetNode::parseMessage(char* msg, int msgLen)
{
  string message(msg), msgType, key, value;
  stringstream msgStream(message);  // convert msg to a stringstream for easy parsing
  string returnstring = "";
  int position;
  // get the actual value of the message first
  key = "";
  while (!msgStream.fail())
  {
    getline(msgStream, msgType);
    //    getline(msgStream, key, "=");
    //    getline(msgStream, key, '=');
    getline(msgStream, key);
    while(key != "EndMessage"){
      position = key.find('=');
      value = key.substr(position + 1);
      cout << "Key: " << key << '\n';
      cout << "Value: " << value << '\n';
      getline(msgStream, key);
    }
    //TODO
    // must make a new node / save stats here (or store temporarily)
  }

  return returnstring;
}
